//#include <cstdio>
//#include <iostream>
//#include <fstream>
//#include <cstring>
//#include <vector>
#include <sstream>

#include <cassert>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <time.h>

#include "leveldb/db.h"
#include "leveldb/options.h"
#include "leveldb/env.h"
#include "leveldb/table.h"
#include "leveldb/iterator.h"
#include "leveldb/slice.h"
#include "db/table_cache.h"

#include "db/filename.h"
#include "db/version_set.h"
#include "db/version_edit.h"
#include "db/dbformat.h"

#include "db/memtable.h"
#include "leveldb/cache.h"
using namespace std;
using namespace leveldb;

//class FileMetaData;
const string DBNAME="finnaldb2";

DB *get_db(const string &file_path) {
    DB *db = nullptr;
    Options op;
    op.create_if_missing = true;
    op.filter_policy=NewBloomFilterPolicy(15);
    op.comparator=NumberComparator();
    Status status = DB::Open(op, file_path, &db);
    assert(status.ok());
    return db;
}

void read_csv_to_db() {
    //open db
//    DB *db = nullptr;
//    Options op;
//    op.create_if_missing = true;
//    Status status = DB::Open(op, "finnaldb", &db);
//    assert(status.ok());
    DB* db=get_db(DBNAME);

    //open file
    ifstream in;
    in.open("data.csv");

    int key, value;
    char seprator;
    for (int i = 0; i < 5000000; i++) {
        in >> key >> seprator >> value;
        db->Put(WriteOptions(), to_string(key), to_string(value));
    }
    delete db;
}

//测试数据库中数据正确 正确值为453,691,750
void test_data_in_db() {
    DB* db=get_db(DBNAME);

    string value;
    db->Get(leveldb::ReadOptions(), "1", &value);
    cout << value;
    db->Get(leveldb::ReadOptions(), "10", &value);
    cout << value;
    db->Get(leveldb::ReadOptions(), "5000000", &value);
    cout << value;
    delete db;
}

int strToInt(const string& s){
    stringstream ss;
    ss<<s;
    int i;
    ss>>i;
//    cout<<i<<endl;
    return i;
}

//在memtable中查找value，打印其key
void findInMemTable(DB* db,string value){
    MemTable* mem=db->GetMemTable();
    mem->printTable(value);
}


int main() {
    // read csv and write into db
//    read_csv_to_db();
//    return 0;
    //测试数据库中数据正确
//    test_data_in_db();
//    return 0;

    int target=750;
    int smallest_key=2500000;
    int largest_key=5000000;

//    int target=749;
//    int smallest_key=1;
//    int largest_key=10000;

    DB *db = get_db("finnaldb");
    leveldb::Version *version = db->GetCurrentVersion();

//    db->Put(WriteOptions(),"5000001",to_string(target));

    //检查memtable
    findInMemTable(db,to_string(target));

    TableCache *table_cache = db->GetTableCache();
    for (int i = 0; i < 7; ++i) {
        std::vector<FileMetaData *> files_ = version->GetFiles(i);
        if (files_.size() > 0) {
            for (int j = 0; j < files_.size(); ++j) {
                FileMetaData* fm=files_[j];
                int smallest=strToInt(files_[j]->smallest.user_key().ToString());
                int largest=strToInt(files_[j]->largest.user_key().ToString());

                //目标在sstable的value范围内
//                if(target>=smallest&&target<=largest){
                if((largest_key>=smallest&&smallest_key<=largest)){
                    //（TODO）bloom filter
                    //获取table
                    Table* table=table_cache->GetTable(fm->number,fm->file_size);
                    //bloom不命中则跳过
                    if(!table->TableKeyMayMatch(to_string(target))) continue;

                    //sstable文件号转文件名示例
//                    string tableFileName=TableFileName("finnaldb",files_[j]->number);
//                    cout<<tableFileName;

                    //迭代获取所有键值对
                    Iterator *it = table_cache->NewIterator(ReadOptions(), files_[j]->number,
                                                            files_[j]->file_size);
                    int k=0;
                    multimap<string,string> m;
                    //用multimap查找目标值
                    for (it->SeekToFirst(); it->Valid(); it->Next(),++k) {
//                        cout << it->key().ToString() << " " << it->value().ToString() <<endl;
                        m.insert(std::make_pair(it->value().ToString(),it->key().ToString()));
                    }
                    auto iter2=m.find(to_string(target));

                    //内部key转userkey
                    const char * a=iter2->second.data();
                    char aa[20]={0};
                    strncpy(aa,a,iter2->second.size()-8);
                    cout<<aa<<endl;
//                    string s=string(a,iter2->second.size()-8);
//                    cout<<s<<endl;
//                    cout<<iter2->second<<endl;
                }

            }


        }
    }

}

//homework 6
//int main(){
//    string fileName="testdb/000005.ldb";
//
//    //footer
//    FILE *fp=fopen(fileName.c_str(),"r");
//    fseek(fp,-48L,SEEK_END);
//    char *footer=new char[48];
//    fgets(footer,48,fp);
//    uint64_t metaOffset,metaSize,indexOffset,indexSize;
//    footer=GetVarint64(footer,10,&metaOffset);
//    footer=GetVarint64(footer,10,&metaSize);
//    footer=GetVarint64(footer,10,&indexOffset);
//    footer=GetVarint64(footer,10,&indexSize);
//    cout<<"Meta offset: "<<metaOffset<< endl
//        <<"Meta size: "<< metaSize <<endl
//        <<"Index offset: "<< indexOffset<< endl
//        <<"Index size: "<<indexSize<<endl;
//
//    char *index_block=new char[indexSize+1];
//    fseek(fp,indexOffset,SEEK_SET);
//    fread(index_block, sizeof(char),indexSize,fp);
//
//    //read the number of block index
//    indexSize-=1;
//    uint32_t numberofindex=0,k=0;
//    while (k<4){
//        numberofindex=numberofindex<<8| (uint32_t)index_block[indexSize];
//        k++;
//        indexSize--;
//    }
//    //read the index block
//    int i=0;
//    vector<BlockIndex> bx;
//    while(i<numberofindex){
//        index_block++;//shared length
//        uint32_t key_length,value_length,bytes;
//        index_block=GetVarint32(index_block,5,&key_length,bytes);//no_shared length
//        key_length-=8;
//        BlockIndex now(key_length+1);
//        index_block=GetVarint32(index_block,5,&value_length,bytes);//value_length
//
//        now.key_length=key_length;
//        now.value_length=value_length;
//        memcpy(now.limit_key,index_block,key_length);//limit_key
//        index_block+=key_length;
//        index_block+=8;
//        cout<< "key_length: "<< key_length
//            << "limit_key: "<< now.limit_key<<endl;
//
//        uint64_t  block_offset, block_size;
//        index_block = GetVarint64(index_block, 10, &block_offset);
//        index_block = GetVarint64(index_block, 10, &block_size);
//        now.offset = block_offset, now.size=block_size;
//        cout<< "block_offset: "<< block_offset
//            <<"block_size: "<< block_size<< endl<<endl;
//
//        bx.push_back(now), i++;
//        bx.push_back(now), i++;
//    }
//    cout<< "read the block: \n\n";
//    for(int i=0;i<numberofindex;i++){
//        uint64_t  block_offset = bx[i].offset, block_size=bx[i].size;
//
//
//        uint64_t  limit_offset = block_offset+block_size-1;
//        char * star_number=new char();
//        uint32_t numberofrestart=0, k=0;
//        while (k<4){
//            fseek(fp, limit_offset, SEEK_SET);
//            fread(star_number, sizeof(char), 1, fp);
//            numberofrestart = numberofrestart<<8 | (uint32_t)* star_number;
//            k ++;
//            limit_offset--;
//        }
//        cout<<"numberofrestart: "<<numberofrestart<<endl;
//        limit_offset -=numberofrestart*4;
//
//        fseek(fp, block_offset, SEEK_SET);
//        string lastkey="";
//
//        while(block_offset<=limit_offset){
//            char* length=new char[5];
//            uint32_t  shared_length, no_shared_length, value_length, bytes;
//            fread(length, sizeof(char), 5, fp);
//            GetVarint32(length, 5, &shared_length, bytes);
//
//            block_offset+=bytes;
//            fseek(fp, block_offset, SEEK_SET);
//            fread(length, sizeof(char), 5,fp);
//            GetVarint32(length, 5, &no_shared_length, bytes);
//            no_shared_length-=8;
//
//            block_offset+=bytes;
//            fseek(fp, block_offset, SEEK_SET);
//            fread(length, sizeof(char), 5, fp);
//            GetVarint32(length, 5, &value_length, bytes);
//            block_offset+=bytes;
//            cout<< "shared_length: "<< shared_length
//                <<"no_shared_length: "<<no_shared_length
//                <<"value length: "<< value_length<<endl;
//
//            char *key=new char[no_shared_length+1];
//            memset(key, '\0', sizeof(*key));
//            fseek(fp, block_offset, SEEK_SET);
//            fread(key, sizeof(char), no_shared_length, fp);
//            string nowkey;
//            if(shared_length!=0)
//                nowkey=lastkey.substr(0, shared_length) + string(key);
//            else{
//                nowkey=key;
//            }
//
//            cout<<"key: "<< nowkey << endl;
//            lastkey = nowkey;
//
//            block_offset +=no_shared_length;
//            char value_type;
//            fseek(fp, block_offset, SEEK_SET);
//            fread(&value_type, sizeof(char), 1, fp);
//            if(value_type==0)
//                cout<<"del\n";
//            else if(value_type==1)
//                cout<< "value_type\n";
//
//            block_offset +=8;
//            char value[value_length+1];
//            memset(value, '\0', sizeof(value));
//            fseek(fp, block_offset, SEEK_SET);
//
//            fread(&value, sizeof(char), value_length, fp);
//            cout<<"value: "<<value<<endl<<endl;
//            block_offset +=value_length;
//
//        }
//    }
//    return 0;
//}
